/*
 * Copyright (c) 2015. 石头哥哥
 */

package com.common.httpclient;
/*

                  写字楼里写字间，写字间里程序员；
                  程序人'猿'写程序，又拿程序换酒钱。
                  酒醒只在网上坐，酒醉还来网下眠；
                  酒醉酒醒日复日，网上网下年复年。
                  但愿老死电脑间，不愿鞠躬老板前；
                  奔驰宝马贵者趣，公交自行程序员。
                  别人笑我忒疯癫，我笑自己命太贱；
                  不见满街漂亮妹，哪个归得程序'猿'？
━━━━━━神兽出没━━━━━━
 ┏┓   ┏┓
┏┛┻━━━┛┻┓
┃       ┃
┃   ━   ┃
┃ ┳┛ ┗┳ ┃
┃       ┃
┃   ┻   ┃
┃       ┃
┗━┓   ┏━┛
  ┃   ┃                    神兽保佑, 永无BUG!
  ┃   ┃       Code is far away from bug with the animal protecting
  ┃   ┗━━━┓
  ┃       ┣┓
  ┃       ┏┛
  ┗┓┓┏━┳┓┏┛
   ┃┫┫ ┃┫┫
   ┗┻┛ ┗┻┛
 */

import com.google.gson.Gson;
import com.google.protobuf.Message;
import com.google.protobuf.MessageLite;
import com.squareup.okhttp.*;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * Created by 石头哥哥 on 12/18/15.
 * <p>
 * common-httpclient
 * <p>
 * com.commons.httpclient
 * <p>
 * comments:  http 操作接口   基于okhttp
 * <p>
 * HttpClient 作为单例  使用  线程安全----- 可采用线程池来处理
 * 多文件上传,也可以采用异步上传
 * 处理Callback 接口
 */
public class HttpClient {

    /**
     * gson 作为json解析器
     */
    private final static Gson gson = new Gson();

    /**
     *
     */
    private OkHttpClient okHttpClient = null;

    private int connectTimeout = 10000;
    private int readTimeout = 10000;
    private int writeTimeout = 10000;

    private static final ConcurrentHashMap<Class<?>, Method> methodCache = new ConcurrentHashMap<Class<?>, Method>();

    /**
     * 连接
     * 读取超时
     * 写超时  采用默认值
     */
    public HttpClient() {
        this.okHttpClient = new OkHttpClient();
        this.okHttpClient.setConnectTimeout(connectTimeout, TimeUnit.SECONDS);
        this.okHttpClient.setReadTimeout(readTimeout, TimeUnit.SECONDS);
        this.okHttpClient.setWriteTimeout(writeTimeout, TimeUnit.SECONDS);
    }


    /**
     * 初始化 操作接口
     *
     * @param connectTimeout
     * @param readTimeout
     * @param writeTimeout
     */
    public HttpClient(int connectTimeout, int readTimeout, int writeTimeout) {
        this.connectTimeout = connectTimeout;
        this.readTimeout = readTimeout;
        this.writeTimeout = writeTimeout;
        this.okHttpClient = new OkHttpClient();

        this.okHttpClient.setConnectTimeout(connectTimeout, TimeUnit.SECONDS);
        this.okHttpClient.setReadTimeout(readTimeout, TimeUnit.SECONDS);
        this.okHttpClient.setWriteTimeout(writeTimeout, TimeUnit.SECONDS);
    }


    /**
     * Create a new {@code Message.Builder} instance for the given class.
     * <p>This method uses a ConcurrentHashMap for caching method lookups.
     */
    private Message.Builder getMessageBuilder(Class<? extends Message> clazz) throws Exception {
        Method method = methodCache.get(clazz);
        if (method == null) {
            method = clazz.getMethod("newBuilder");
            methodCache.put(clazz, method);
        }
        return (Message.Builder) method.invoke(clazz);
    }


    /**
     * @param url
     * @param response_clazz 响应pb 类
     * @return
     * @throws Exception
     */
    public Message mergeFrom(String url, Class<? extends Message> response_clazz) throws Exception {
        byte[] data = get_bytes(url);
        return data != null && data.length != 0 ? getMessageBuilder(response_clazz).mergeFrom(data).build() : null;
    }

    /**
     * application/x-protobuf
     *
     * @param url
     * @param response_builder merge   响应 builder
     * @return 协议中 制定的消息实体
     * @throws IOException
     */
    public Message mergeFrom(String url, Message.Builder response_builder) throws IOException {
        byte[] data = get_bytes(url);
        return data != null && data.length != 0 ? response_builder.mergeFrom(data).build() : null;
    }


    /**
     * application/x-protobuf ,application/json
     * get 方式传递数据
     * content-type : text/plain
     *
     * @param url
     * @return 协议中 制定的消息实体字节流
     * @throws IOException
     */
    public byte[] get_bytes(String url) throws IOException {
        Request request = new Request.Builder().url(url)
                .get().build();
        Response response = okHttpClient.newCall(request).execute();
        return response.body().bytes();
    }


    /**
     * application/x-protobuf
     * post 方式传递数据
     * content-type : application/x-protobuf
     *
     * @param url
     * @param request_builder 请求数据
     * @param response_clazz  响应获取数据pb实体类
     * @return 协议中 制定的消息实体
     * @throws IOException
     */
    public Message mergeFrom(String url,
                             MessageLite.Builder request_builder
            , Class<? extends Message> response_clazz) throws Exception {
        byte[] data = builder(url, request_builder);
        return data != null && data.length != 0 ? getMessageBuilder(response_clazz).mergeFrom(data).build() : null;
    }


    /**
     * application/x-protobuf
     * post 方式传递数据
     * content-type : application/x-protobuf
     *
     * @param url
     * @param request_builder  请求数据pb实体类型builder
     * @param response_builder 响应获取数据pb实体builder
     * @return 协议中 制定的消息实体
     * @throws IOException
     */
    public MessageLite mergeFrom(String url
            , MessageLite.Builder request_builder
            , MessageLite.Builder response_builder) throws IOException {
        byte[] data = builder(url, request_builder);
        return data != null && data.length != 0 ? response_builder.mergeFrom(data).build() : null;
    }


    /**
     * 接收类型 和请求类型
     * application/x-protobuf
     * <p>
     * post 方式传递数据
     * content-type : application/x-protobuf
     * accept :  application/x-protobuf
     *
     * @param url
     * @param request_builder
     * @return 协议中 制定的消息实体字节流
     * @throws IOException
     */
    public byte[] builder(String url, MessageLite.Builder request_builder) throws IOException {
        //build body
        RequestBody body = RequestBody.create(MediaType.parse("application/x-protobuf;charset=utf-8"),
                request_builder.build().toByteArray());
        Request request = new Request.Builder()
                .addHeader("Content-Type", "application/x-protobuf")
                .addHeader("Accept", "application/x-protobuf")
                .url(url)
                .post(body).build();
        Response response = okHttpClient.newCall(request).execute();
        return response.body().bytes();
    }


    /**
     * text/plain
     * <p>
     * 同步 提交数据
     * text/plain 数据
     * <p>
     * 返回json对象
     *
     * @param url    host:port/path  http://192.168.0.128:10005/terminal/add
     * @param object
     * @return <><>T is json object vo</></>
     */
    public <T> T get(String url, Object object, Class<T> _class) throws IOException {

        Request request = new Request.Builder().url(url)
                .get().build();

        Response response = okHttpClient.newCall(request).execute();

        return gson.fromJson(response.body().string(), _class);
    }


    /**
     * text/plain
     * <p>
     * POST提交键值对
     * 构建键值对数据
     * FormEncodingBuilder builder = new FormEncodingBuilder()
     * builder.add("platform", "android")
     * builder.add("name", "bug")
     * builder.add("subject", "XXXXXXXXXXXXXXX")
     *
     * @param url
     * @param builder
     * @param _class
     * @param <T>
     * @return
     * @throws IOException
     */
    public <T> T postByForm(String url, FormEncodingBuilder builder, Class<T> _class) throws IOException {
        Request request = new Request.Builder().url(url).post(builder.build()).build();
        Response response = okHttpClient.newCall(request).execute();
        return gson.fromJson(response.body().string(), _class);
    }


    /**
     * application/json
     * 同步 提交数据
     * POST提交Json数据
     * <p>
     * 返回json对象
     *
     * @param url
     * @param object
     * @return <><>T is json object vo</></>
     */
    public <T> T postByJson(String url, Object object, Class<T> _class) throws IOException {
        RequestBody body = RequestBody.create(MediaType.parse("application/json;charset=utf-8"), gson.toJson(object));
        Request request = new Request.Builder().url(url)
                .post(body).build();

        Response response = okHttpClient.newCall(request).execute();

        return gson.fromJson(response.body().string(), _class);
    }


    /**
     * application/json
     * 异步提交数据
     * <p>
     * POST提交Json数据
     *
     * @param url
     * @param object
     * @param responseCallback 回掉该接口
     * @throws IOException
     */
    public void postByJson(String url, Object object, Callback responseCallback) throws IOException {
        RequestBody body = RequestBody.create(MediaType.parse("application/json;charset=utf-8"), gson.toJson(object));
        Request request = new Request.Builder().url(url)
                .post(body).build();
        okHttpClient.newCall(request).enqueue(responseCallback);
    }


    /**
     * 同步提交数据
     *
     * @param url
     * @param _class
     * @param filePaths
     * @param <T>
     * @return
     * @throws IOException
     */
    public <T> T postFile(String url, Class<T> _class, String... filePaths) throws Exception {
        MultipartBuilder builder = new MultipartBuilder();
        builder.type(MultipartBuilder.FORM);
        for (String filepath : filePaths) {
            File file = new File(filepath);
            String fileName = file.getName();
            builder.addFormDataPart(fileName.substring(0, fileName.indexOf(".")), file.getName(), RequestBody.create(null, file));
        }
        Request request = new Request.Builder().url(url).post(builder.build()).build();
        Response response = okHttpClient.newCall(request).execute();
        if (response.isSuccessful()) {
            return gson.fromJson(response.body().string(), _class);
        } else {
            throw new Exception(response.message());
        }
    }


    /**
     * 处理 上传 文件
     * 异步提交数据
     *
     * @param url
     * @param responseCallback
     * @param filePaths
     */
    public void postFile(String url, Callback responseCallback, String... filePaths) {
        MultipartBuilder builder = new MultipartBuilder();
        builder.type(MultipartBuilder.FORM);
        for (String filepath : filePaths) {
            File file = new File(filepath);
            String fileName = file.getName();
            builder.addFormDataPart(fileName.substring(0, fileName.indexOf(".")), file.getName(), RequestBody.create(null, file));
        }
        Request request = new Request.Builder().url(url).post(builder.build()).build();
        okHttpClient.newCall(request).enqueue(responseCallback);
    }


    /**
     * 异步下载文件
     *
     * @param url
     * @param responseCallback
     */
    public void asyncDownLoadFile(String url, Callback responseCallback) {
        Request request = new Request.Builder().url(url).get().build();
        okHttpClient.newCall(request).enqueue(responseCallback);
    }


    /**
     * 同步下载文件
     *
     * @param url
     */
    public Response downLoadFile(String url) throws IOException {
        Request request = new Request.Builder().url(url).get().build();
        return okHttpClient.newCall(request).execute();
    }


//    public int getConnectTimeout() {
//        return connectTimeout;
//    }
//
//    public void setConnectTimeout(int connectTimeout) {
//        this.connectTimeout = connectTimeout;
//    }
//
//    public int getReadTimeout() {
//        return readTimeout;
//    }
//
//    public void setReadTimeout(int readTimeout) {
//        this.readTimeout = readTimeout;
//    }
//
//    public int getWriteTimeout() {
//        return writeTimeout;
//    }
//
//    public void setWriteTimeout(int writeTimeout) {
//        this.writeTimeout = writeTimeout;
//    }
}
